package com.bracketbird.server.repository.tournament;

import com.bracketbird.client.model.keys.*;
import com.bracketbird.client.service.*;
import com.bracketbird.client.service.rtc.*;
import com.bracketbird.server.dao.*;
import com.bracketbird.server.jdo.club.*;
import com.bracketbird.server.repository.*;
import com.google.appengine.api.datastore.*;
import com.bracketbird.clientcore.appcontrol.*;
import com.bracketbird.clientcore.util.*;

import java.util.*;

/**
 *
 */
public class RTCHelper {
    private RTCEventConverter rtcConv = new RTCEventConverter();
    private Dao<RTCEventJDO> rtcDao = new Dao<RTCEventJDO>(RTCEventJDO.class);

    public RTCHelper() {
    }


    public RTCResponse updateRTCEventLog(RTCRequest req) throws ApplicationException {

        RTCEvent event = req.getEvent();
        List<RTCEventJDO> jdos = CU.convert(rtcDao.findBy(FinderFac.findByTournamentAndIdEqualOrGreaterThan(req.getEvent().getTournamentId(), req.getLastServerLogId())));
        validateDataConsistence(req.getLastServerLogId(), jdos);

        //remove the object that corresponds to changelog in the event.
        jdos.remove(0);

        if (clientIsUpdToDate(jdos)) {
            //System.out.println("SERVER: SUCCES");
            RTCEvent e = createlogEvent(req.getLastServerLogId(), event, false);
            return createResponse(e, RTCResponse.Status.succes);
        }
        else {


            //able to merge (ei. 'overwrite') - clients change is added last.

            if (isSameState(event, jdos) && modelObjectNotDeleted(event, jdos)) {

                //System.out.println("SERVER: MERGE");
                RTCEvent e = createlogEvent(CU.lastItem(jdos).getId(), event, false);
                List<RTCEvent> retList = rtcConv.convert(jdos);
                retList.add(e);
                return createResponse(retList, RTCResponse.Status.merged);

            }
            else {
                //System.out.println("SERVER: NOT MERGABLE");

                //not able to merge - client has to bring itself up to date.
                return new RTCResponse(RTCResponse.Status.not_mergable, rtcConv.convert(jdos));
            }
        }

    }


    private RTCEvent createlogEvent(Long lastLogId, RTCEvent event, boolean newState) {
        System.out.println("CreateEvent Log: "+lastLogId + 1+". " +event);
        RTCEventJDO jdo = rtcConv.convert(event, lastLogId + 1);
        return rtcConv.convert(rtcDao.create(jdo));
    }

    private RTCResponse createResponse(List<RTCEvent> eList, RTCResponse.Status status) {
        return new RTCResponse(status, eList);


    }


    private RTCResponse createResponse(RTCEvent e, RTCResponse.Status status) {
        List<RTCEvent> eList = new ArrayList<RTCEvent>();
        eList.add(e);
        return new RTCResponse(status, eList);


    }


    private boolean modelObjectNotDeleted(RTCEvent event, List<RTCEventJDO> jdos) {
        Key modelKey = KeyFac.convert(event.getModelId());
        if (modelKey == null) {
            return true;
        }
        for (RTCEventJDO jdo : jdos) {
            if (modelKey.equals(jdo.getModelKey())) {
                if (isDeleteEvent(jdo)) {
                    return false;
                }
            }
        }
        return true;
    }

    private boolean isDeleteEvent(RTCEventJDO jdo) {
        return jdo.getEventName().toLowerCase().startsWith("delete");
    }

    private boolean isSameState(RTCEvent event, List<RTCEventJDO> jdos) {
        for (RTCEventJDO jdo : jdos) {
            if (jdo.getState() != event.getState().intValue()) {
                return false;
            }
        }
        return true;
    }

    private boolean clientIsUpdToDate(List<RTCEventJDO> jdos) {
        return jdos.size() == 0;
    }

    private void validateDataConsistence(long lastChangeLog, List<RTCEventJDO> jdos) {
        if (jdos.size() == 0) {
            throw new SystemException("Data inconsistence between client and server - unable to find any RTCEventJDOs from Event.getLastChangeLog=" + lastChangeLog);
        }
        if (jdos.size() > 0) {
            //changelog of first jdo has to be the same as changelog in event
            if (jdos.get(0).getId() != lastChangeLog) {
                throw new SystemException("Data inconsistence or finder error - changelog not the same. jdo.changelog=" + jdos.iterator().next().getId() + " event.changelog=" + lastChangeLog);
            }
        }

    }


    public List<RTCEvent> getEventLog(TournamentId tId) {
        Collection<RTCEventJDO> jdos = rtcDao.findBy(FinderFac.findByEntityId(tId));

        return new RTCEventConverter().convert(jdos);
    }


    public List<RTCEvent> getEventLog(TournamentId tId, Long fromId) {
        List<RTCEventJDO> jdos = CU.convert(rtcDao.findBy(FinderFac.findByTournamentAndIdEqualOrGreaterThan(tId, fromId)));
        jdos.remove(0);
        return new RTCEventConverter().convert(jdos);
    }


    public void deleteBadEvents(TournamentId tId, Long lastSuccesId) {
        List<RTCEventJDO> jdos = CU.convert(rtcDao.findBy(FinderFac.findByTournamentAndIdGreaterThan(tId, lastSuccesId)));
        for (RTCEventJDO jdo : jdos) {
            jdo.setDeleted(true);
        }
        rtcDao.updateAll(jdos);
    }
}
